/**************************************************************************
*   Copyleft 2009 by        											  *
*	Iwanicki Krzysztof													  *
*   <www.gamma.mini.pw.edu.pl/~iwanickik>		                          *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
***************************************************************************/
#include "./vFreeLookCamera.h"
#include "../vEngine3D.h"

namespace Vendetta {
	vVoid vFreeLookCamera::Update(const vFrameListener::vFrameEvent& event) {
		mRightDir = CrossProduct(mTarget - mPosition, mUpDir);
		mRightDir.Normalize();

		if (mForwardKeyDown) {
			move(mSpeed.X);
		}
		if (mBackwardKeyDown) {
			move(-mSpeed.X);
		}
		if (mStrafeLeftKeyDown) {
			strafe(-mSpeed.X);
		}
		if (mStrafeRightKeyDown) {
			strafe(mSpeed.X);
		}
	}

	vVoid vFreeLookCamera::MouseMoved(const vMouseListener::MouseEvent& event) {
		vReal x, y;
		vReal angleY, angleZ;

		x = (vReal) event.mPos.x;
		y = (vReal) event.mPos.y;

		if(mWasLeftButtonDown) {
			angleY = (vReal)((mLastMouseCoords.X - x)) / 500.0;
			angleZ = (vReal)((mLastMouseCoords.Y - y)) / 500.0;

			mCurrentRot -= angleZ;
			if (mCurrentRot > 0.9999) {
				mCurrentRot = 0.9999;
			} else if (mCurrentRot < -0.9999) {
				mCurrentRot = -0.9999;
			} else {
				vVector3D axis;
				axis = CrossProduct(mTarget - mPosition, mUpDir);
				axis.Normalize();
				rotateView(angleZ, axis);
				rotateView(angleY, vVector3D::UnitY);
			}
		}

		mLastMouseCoords.X = x;
		mLastMouseCoords.Y = y;
	}

	vVoid vFreeLookCamera::MousePressed(const vMouseListener::MouseEvent& event) {
		if (event.mButton == V_MB_LEFT) {
			mWasLeftButtonDown = true;
		}
	}

	vVoid vFreeLookCamera::MouseReleased(const vMouseListener::MouseEvent& event) {
		if (event.mButton == V_MB_LEFT) {
			mWasLeftButtonDown = false;
		}
	}

	vVoid vFreeLookCamera::KeyPressed(const vKeyboardListener::KeyboardEvent& event) {
		switch (event.mVirtualKeyCode) {
			default:
				return;
			case 0x41:		// A
				mStrafeLeftKeyDown = true;
				break;
			case 0x53:		// S
				mBackwardKeyDown = true;
				break;
			case 0x44:		// D
				mStrafeRightKeyDown = true;
				break;
			case 0x57:		// W
				mForwardKeyDown = true;
				break;
		}
	}

	vVoid vFreeLookCamera::KeyReleased(const vKeyboardListener::KeyboardEvent& event) {
		switch (event.mVirtualKeyCode) {
			default:
				return;
			case 0x41:		// A
				mStrafeLeftKeyDown = false;
				break;
			case 0x53:		// S
				mBackwardKeyDown = false;
				break;
			case 0x44:		// D
				mStrafeRightKeyDown = false;
				break;
			case 0x57:		// W
				mForwardKeyDown = false;
				break;
		}
	}

	vFreeLookCamera::vFreeLookCamera(const vString& name): vCamera(name) {
		vEngine3D::InstancePtr()->InputSystemPtr()->AddMouseListener(this);
		vEngine3D::InstancePtr()->InputSystemPtr()->AddKeybordListener(this);
		mWasLeftButtonDown = false;
		mCurrentRot = 0;
		mBackwardKeyDown = false;
		mForwardKeyDown = false;
		mStrafeLeftKeyDown = false;
		mStrafeRightKeyDown = false;
		mSpeed.X = 0.1;
	}
	
	vFreeLookCamera::~vFreeLookCamera() {
		// TODO: Check why error rises when removing from mouse and keyboard listener
		//vEngine3D::InstancePtr()->InputSystemPtr()->RemoveMouseListener(this);
		//vEngine3D::InstancePtr()->InputSystemPtr()->RemoveKeybordListener(this);
	}

	vVoid vFreeLookCamera::rotateView(vReal rAngle, const vVector3D& axis) {
		vVector3D vNewView;

		// get the view vector
		vVector3D vView(mTarget - mPosition);

		// calculate the sine and cosine of the angle
		vReal cosTheta = Cos(rAngle);
		vReal sinTheta = Sin(rAngle);

		// find the new x position for the new rotated point
		vNewView[0] = (cosTheta + (1 - cosTheta) * axis[0] * axis[0])
			* vView[0];
		vNewView[0] += ((1 - cosTheta) * axis[0] * axis[1] - axis[2] * sinTheta)
			* vView[1];
		vNewView[0] += ((1 - cosTheta) * axis[0] * axis[2] + axis[1] * sinTheta)
			* vView[2];

		// find the new y position for the new rotated point
		vNewView[1] = ((1 - cosTheta) * axis[0] * axis[1] + axis[2] * sinTheta)
			* vView[0];
		vNewView[1] += (cosTheta + (1 - cosTheta) * axis[1] * axis[1])
			* vView[1];
		vNewView[1] += ((1 - cosTheta) * axis[1] * axis[2] - axis[0] * sinTheta)
			* vView[2];

		// Find the new z position for the new rotated point
		vNewView[2] = ((1 - cosTheta) * axis[0] * axis[2] - axis[1] * sinTheta)
			* vView[0];
		vNewView[2] += ((1 - cosTheta) * axis[1] * axis[2] + axis[0] * sinTheta)
			* vView[1];
		vNewView[2] += (cosTheta + (1 - cosTheta) * axis[2] * axis[2])
			* vView[2];

		// set the target
		mTarget = mPosition + vNewView;
	}

	vVoid vFreeLookCamera::strafe(vReal rSpeed) {
		// modify position
		mPosition[0] += mRightDir[0] * rSpeed;
		mPosition[2] += mRightDir[2] * rSpeed;

		// and target
		mTarget[0] += mRightDir[0] * rSpeed;
		mTarget[2] += mRightDir[2] * rSpeed;
	}

	vVoid vFreeLookCamera::move(vReal rSpeed) {
		// get the current view vector
		vVector3D vector(mTarget - mPosition);
		vector.Normalize();
		vector *= rSpeed;

		mPosition += vector;
		mTarget += vector;
	}

	/************************************************************************/
	/* Disabled                                                             */
	/************************************************************************/
	vFreeLookCamera::vFreeLookCamera() {
		V_CHECK_MSG(0, "Some tries to call vFreeLookCamera defauly ctor.") ;
	}

	vFreeLookCamera::vFreeLookCamera(const vFreeLookCamera& camera) { 
		V_CHECK_MSG(0, "Some tries to call vFreeLookCamera copy ctor.") ;
	}

	vFreeLookCamera& vFreeLookCamera::operator=(const vFreeLookCamera& camera) { 
		V_CHECK_MSG(0, "Some tries to call vFreeLookCamera operator=."); 
		return * this; 
	}
	/************************************************************************/
}